package dao

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"reflect"
	"strings"

	"github.com/google/wire"
	"gorm.io/gorm"

	"github.com/LyricTian/gin-admin/v8/internal/app/config"
	"github.com/LyricTian/gin-admin/v8/internal/app/dao/demo"
	"github.com/LyricTian/gin-admin/v8/internal/app/dao/menu"
	"github.com/LyricTian/gin-admin/v8/internal/app/dao/role"
	"github.com/LyricTian/gin-admin/v8/internal/app/dao/user"
	"github.com/LyricTian/gin-admin/v8/internal/app/dao/util"
	"github.com/LyricTian/gin-admin/v8/internal/app/tools"
	"github.com/LyricTian/gin-admin/v8/pkg/util/yaml"
) // end

// RepoSet repo injection
var RepoSet = wire.NewSet(
	util.TransSet,
	menu.MenuActionResourceSet,
	menu.MenuActionSet,
	menu.MenuSet,
	role.RoleMenuSet,
	role.RoleSet,
	user.UserRoleSet,
	user.UserSet,
	demo.DemoSet,
) // end

// Define repo type alias
type (
	TransRepo              = util.Trans
	MenuActionResourceRepo = menu.MenuActionResourceRepo
	MenuActionRepo         = menu.MenuActionRepo
	MenuRepo               = menu.MenuRepo
	RoleMenuRepo           = role.RoleMenuRepo
	RoleRepo               = role.RoleRepo
	UserRoleRepo           = user.UserRoleRepo
	UserRepo               = user.UserRepo
	DemoRepo               = demo.DemoRepo
) // end

type modelAuto struct {
	Model   interface{}
	Comment string
}

// Auto migration for given models
func AutoMigrate(db *gorm.DB) error {
	if dbType := config.C.Gorm.DBType; strings.ToLower(dbType) == "mysql" {
		db = db.Set("gorm:table_options", "ENGINE=InnoDB")
	}

	models := []modelAuto{

		// db.Set("gorm:table_options", "comment '用户地址表' ").AutoMigrate(&Address{})
		// return db.AutoMigrate(
		// 	new(menu.MenuActionResource),
		// 	new(menu.MenuAction),
		// 	new(menu.Menu),
		// 	new(role.RoleMenu),
		// 	new(role.Role),
		// 	new(user.UserRole),
		// 	new(user.User),
		// 	new(demo.Demo),
		// ) // end

		{&menu.MenuActionResource{}, ""},
		{&menu.MenuAction{}, ""},
		{&menu.Menu{}, ""},
		{&role.RoleMenu{}, ""},
		{&role.Role{}, ""},
		{&user.UserRole{}, ""},
		{&user.User{}, ""},

		{&demo.Demo{}, ""},
	} // end

	if config.C.RunMode != "test" {
		// 数据迁移
		initialDataFile := "./autoModel/"
		files, err := ioutil.ReadDir(initialDataFile)
		if err != nil {
			log.Fatal(err)
		}
		// var myFile []fs.FileInfo
		for _, file := range files {
			if file.IsDir() {
				continue
			}
			// ReadStruct(injector, ctx, initialDataFile+file.Name())
			for d_i, d_val := range models {
				modelVal := reflect.TypeOf(d_val.Model)
				if modelVal.Kind() == reflect.Ptr {
					modelVal = modelVal.Elem()
				}

				fileNameWithSuffix := path.Base(initialDataFile + file.Name())
				//获取文件的后缀(文件类型)
				fileType := path.Ext(fileNameWithSuffix)
				//获取文件名称(不带后缀)
				fileNameOnly := strings.TrimSuffix(fileNameWithSuffix, fileType)
				if modelVal.Name() == tools.Marshal(fileNameOnly) {
					res, err := readData(initialDataFile + file.Name())

					if err != nil {
						continue
					}

					models[d_i].Comment = res.Comment
				}
			}

		}

		for _, val := range models {

			err := db.Set("gorm:table_options", "comment '"+val.Comment+"'").AutoMigrate(val.Model)
			if err != nil {
				return err

			}
		}
	} else {
		for _, val := range models {
			err := db.AutoMigrate(val.Model)
			if err != nil {
				return err

			}
		}
	}

	// db.Set("gorm:table_options", "comment '用户地址表' ").AutoMigrate(&Address{})
	// return db.AutoMigrate(
	// 	new(menu.MenuActionResource),
	// 	new(menu.MenuAction),
	// 	new(menu.Menu),
	// 	new(role.RoleMenu),
	// 	new(role.Role),
	// 	new(user.UserRole),
	// 	new(user.User),
	// 	new(demo.Demo),
	// ) // end
	return nil
}

type modelComment struct {
	StructName  string                   `yaml:"name"`        // 结构体名称
	DefaultData []map[string]interface{} `yaml:"defaultData"` // 默认数据
	Comment     string                   `yaml:"comment"`     // 注释
	Fields      []map[string]interface{} `yaml:"fields"`      // 字段项
}

func readData(name string) (modelComment, error) {
	var data modelComment
	file, err := os.Open(name)
	if err != nil {
		return data, err
	}
	defer file.Close()
	// d := util.YAMLNewDecoder(file)
	d := yaml.NewDecoder(file)
	d.SetStrict(true)
	err = d.Decode(&data)
	return data, err
}
